#include <cstdio>
#include <sstream>
#include <algorithm>
#include <unistd.h>
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Tooling/Tooling.h"

#include "ClassData.hpp"

#define VERBOSE 0
#if VERBOSE
#define TRACE() \
	do { \
		::printf("%s\n",__FUNCTION__);\
	} while (0)
#define DEBUGPRINT ::printf
#else
#define TRACE()
#define DEBUGPRINT(...)
#endif
using namespace clang;

static inline std::vector<std::string> split(const std::string& str,char delm)
{
	std::vector<std::string> elems;
	std::stringstream ss(str);
	std::string item;
	while (getline(ss,item,delm)) {
		if (!item.empty()) {
			elems.push_back(item);
		}
	}
	return elems;
}

static bool startswith(const std::string& str, const std::string& start){
    int srclen = str.size();
    int startlen = start.size();
    if(srclen >= startlen)
    {
        std::string temp = str.substr(0, startlen);
        if(temp == start)
            return true;
    }

    return false;
}

static std::string showTemplateArgs(const clang::TemplateArgument* aTargs)
{
	std::string buf;
	llvm::raw_string_ostream os(buf);
	aTargs->dump(os);
	return os.str();
}

static std::string _asToStr(clang::AccessSpecifier aAs) {
	switch (aAs) {
		case AS_public:
			return "public";
		case AS_protected:
			return "protected";
		case AS_private:
			return "private";
		default:
			return "";
	}
}

class LocationFacade/*{{{*/
{
	private:
		std::string mLocStr;
		static const int FILENAME = 0;
		static const int LINE = 1;
		static const int COL = 2;
	public:
		LocationFacade(const SourceManager& aSM,const SourceLocation& aLoc)
			:mLocStr(aLoc.printToString(aSM)){ }
		const std::string getFileName() {
			return split(mLocStr,':')[0];
		}
};/*}}}*/

class CXXRecordDeclFacade
{
private:
	CXXRecordDecl* mCXXRecordDecl;
	PrintingPolicy mPolicy;
public:

	CXXRecordDeclFacade(CXXRecordDecl* aCXXRecordDecl,PrintingPolicy& aPolicy)
		: mCXXRecordDecl(aCXXRecordDecl),mPolicy(aPolicy) {}

	std::vector<std::string> showAllBaseClasses() {
		std::vector<std::string> ret;
		for (auto&& b : mCXXRecordDecl->bases()) {
			const clang::Type* t = b.getType().getTypePtr();
			std::string typeStr = t->getAsCXXRecordDecl()?
				t->getAsCXXRecordDecl()->getNameAsString():"error";
			ret.push_back(typeStr);
			::printf("base class:%s\n",ret.back().c_str());
		}
		return ret;
	}

	std::vector<FunctionDesc> getAllFunction() {
		std::vector<FunctionDesc> ret;
		for (auto&& m : mCXXRecordDecl->methods()) {
			FunctionDesc desc;
			desc.access = _asToStr(m->getAccess());
			desc.name = m->getNameAsString();
			desc.type = m->getReturnType().getAsString(mPolicy);
			ret.push_back(desc);
		}
		return ret;
	}
};

class FieldDeclFacade
{
private:
		FieldDecl* mFieldDecl;
public:
		FieldDeclFacade(FieldDecl* aFieldDecl)
			: mFieldDecl(aFieldDecl){}
		std::string showParent() {
			return mFieldDecl->getParent()->getNameAsString();
		}
		std::string showName() {
			return mFieldDecl->getNameAsString();
		}
#if 0
		std::string showType() {
			const clang::Type* t = _getType();
			return (t->getPointeeCXXRecordDecl())?
				t->getPointeeCXXRecordDecl()->getNameAsString():
				mFieldDecl->getType().getAsString(Policy);
		}
#endif
		const TemplateSpecializationType* getTemplateSpecializationType() {
			return _getType()->getAs<clang::TemplateSpecializationType>();
		}
private:
		const clang::Type* _getType() {
			return mFieldDecl->getType().getTypePtr();
		}
};

class ClassDiagramVisitor:public clang::ASTConsumer,public clang::RecursiveASTVisitor<ClassDiagramVisitor>
{
	private:
		PrintingPolicy Policy;
		ASTContext* Context;
		std::string path;
	public:
		explicit ClassDiagramVisitor(ASTContext* ct, std::string p)/*{{{*/
			: Policy(PrintingPolicy(ct->getPrintingPolicy()))
			, Context(ct)
			, path(p){
					Policy.SuppressScope = true;
			}

		void HandleTranslationUnit(clang::ASTContext& Context){
			//TraverseDecl(Context.getTranslationUnitDecl());
			CollectAllClass(Context.getTranslationUnitDecl());
			PrintAllClassLoc(Context.getTranslationUnitDecl());
		}

		void CollectAllClass(DeclContext* aDeclContext) {/*{{{*/
			for (DeclContext::decl_iterator i = aDeclContext->decls_begin(), e = aDeclContext->decls_end(); i != e; i++) {
				Decl* D = *i;
				
				if (NamedDecl *N = dyn_cast<NamedDecl>(D)) {
	
					std::string kind = D->getDeclKindName();
					//std::cout<<kind<<std::endl;
					if(kind == "CXXRecord") {
						
						CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D);
						SourceLocation SL = RD->getLocation();
  						SourceManager &SM = RD->getASTContext().getSourceManager();
						LocationFacade loc(SM,D->getLocation());
						std::string temp = loc.getFileName().c_str();
						//std::cout<<temp<<std::endl;
						if(!startswith(temp,path)||!RD->hasDefinition()){
							continue;						
						}
						std::cout<<temp<<std::endl;
						std::stringstream str;
						str << "CXXRecord:"<< N->getNameAsString();
						std::cout<<str.str().c_str()<<std::endl;
						std::string name = N->getNameAsString();
						Record::maybeAddClass(name);

						CXXRecordDeclFacade CxxRecord =
						   	CXXRecordDeclFacade(dyn_cast<CXXRecordDecl>(D),Policy);

						for (auto&& b : CxxRecord.showAllBaseClasses()) {
							::printf("found base class\n");
							Record::maybeAddClass(b);
							Record::addBase(N->getNameAsString(),b);
						}

						for (auto&& m : CxxRecord.getAllFunction()) {
							Record::addMethod(name,m.name,m.type,m.access);
						}
					}
					/*if(kind == "Namespace") {
						
						NamespaceDecl *RD = dyn_cast<NamespaceDecl>(D);
						SourceLocation SL = RD->getLocation();
  						SourceManager &SM = RD->getASTContext().getSourceManager();
						LocationFacade loc(SM,D->getLocation());
						std::string temp = loc.getFileName().c_str();
						std::cout<<temp<<std::endl;
						if(!startswith(temp,"/home/linpeng/linpeng/test/log4cplus-2.0.5")){
							continue;						
						}
						std::cout<<temp<<std::endl;
						
						

					}*/
				}
			}
		}/*}}}*/


		void PrintAllClassLoc(DeclContext* aDeclContext) {
			TRACE();
			for (DeclContext::decl_iterator i = aDeclContext->decls_begin(), e = aDeclContext->decls_end(); i != e; i++) {
				Decl* D = *i;

				if (NamedDecl *N = dyn_cast<NamedDecl>(D)) {
					std::string kind = D->getDeclKindName();
					if(kind == "CXXRecord") {

						CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D);
						SourceLocation SL = RD->getLocation();
  						SourceManager &SM = RD->getASTContext().getSourceManager();
						LocationFacade loc(SM,D->getLocation());
						std::string temp = loc.getFileName().c_str();
						
						if(!startswith(temp,path)||!RD->hasDefinition()){
							continue;						
						}						
						std::cout<<temp<<std::endl;
						std::stringstream str;
						str << D->getDeclKindName() << " :"<< N->getNameAsString();
					
						TraverseDecl(D);
					}
				}
			}

		}

		bool VisitCXXRecordDecl(CXXRecordDecl* Declaration) {/*{{{*/
			TRACE();
			//EnumerateDecl(Declaration);
			if(Declaration->hasDefinition()){
				Record::maybeAddClass(Declaration->getNameAsString());
			}
			

			return true;
		}/*}}}*/

		bool VisitFieldDecl(FieldDecl* aFieldDecl) {
			TRACE();
			FieldDeclFacade fd(aFieldDecl);

			std::string parent = fd.showParent();
			std::string name = fd.showName();

			std::string type;
			const clang::Type* t = aFieldDecl->getType().getTypePtr();
			if(t->getPointeeCXXRecordDecl()) {
				type = t->getPointeeCXXRecordDecl()->getNameAsString();
			}
			else {
				type = aFieldDecl->getType().getAsString(Policy);
			}

			const clang::TemplateSpecializationType* tmpType = t->getAs<clang::TemplateSpecializationType>();

			// template type
			if(tmpType) {
				bool containTargetClass = false;
				if(int num = tmpType->getNumArgs()) {
					DEBUGPRINT("template type found:%s\n",type.c_str());
					const clang::TemplateArgument* args = tmpType->getArgs();
					for (int i = 0; i < num; i++) {
						DEBUGPRINT("template argument[%d]=%s\n",i,showTemplateArgs(&args[i]).c_str());
						if(Record::isTargetClassType(showTemplateArgs(&args[i]))) {
							Record::addField(parent,name,showTemplateArgs(&args[i]),"",
									true);
							containTargetClass = true;
						}
					}
				}
				if(not containTargetClass) {
					Record::addField(parent,name,type,"", false);
				}
			}

			else {
				Record::addField(parent,name,type,"",Record::isTargetClassType(type));
			}

			DEBUGPRINT("(parent,type,name,isClass)=%s,%s,%s,%s\n",
					parent.c_str(),
					type.c_str(),
					name.c_str(),
					(Record::isTargetClassType(type))?"true":"false");



			return true;
		}
		
		std::map<std::string,Record*> getRecordDB(){
			return Record::recordDB;		
		}
		
		void clearRecordDB(){
			Record::recordDB.clear();
		} 

};


/*class ClassDiagramConsumer:public clang::ASTConsumer{
	public:
		void HandleTranslationUnit(clang::ASTContext& Context){
			Visitor.CollectAllClass(Context.getTranslationUnitDecl());
			Visitor.PrintAllClassLoc(Context.getTranslationUnitDecl());
		}
	private:
		ClassDiagramVisitor Visitor;
};*/


